11.2 性能测试

性能测试函数以Benchmark为名称前缀,同样保存在“*_test.go”文件里。

func add(x,y int)int{ return x+y }

func BenchmarkAdd(b*testing.B) { for i:=0;i<b.N;i++ { _ =add(1,2) } }

输出:

$go test-bench.

BenchmarkAdd-4 2000000000 0.52 ns/op ok test 1.107s

测试工具默认不会执行性能测试,须使用bench参数。它通过逐步调整B.N值,反复执行测试函数,直到能获得准确的测量结果。

func BenchmarkAdd(b*testing.B) { println(“B.N=“,b.N)

for i:=0;i<b.N;i++ { _ =add(1,2) } }

输出:

$go test-bench.

BenchmarkAdd-4

B.N=1 B.N=100 B.N=10000 B.N=1000000 B.N=100000000 B.N=2000000000

2000000000 0.55 ns/op ok test 1.171s

如果希望仅执行性能测试,那么可以用run=NONE忽略所有单元测试用例。

默认就以并发方式执行测试,但可用cpu参数设定多个并发限制来观察结果。

$go test-bench. -cpu 1,2,4

BenchmarkAdd 2000000000 0.52 ns/op BenchmarkAdd-2 2000000000 0.51 ns/op BenchmarkAdd-4 2000000000 0.52 ns/op ok test 3.281s

某些耗时的目标,默认循环次数过少,取平均值不足以准确计量性能。可用benchtime设定最小测试时间来增加循环次数,以便返回更准确的结果。

func sleep() { time.Sleep(time.Second) }

func BenchmarkSleep(b*testing.B) { for i:=0;i<b.N;i++ { sleep() } }

输出:

$go test-bench. -benchtime 5s

BenchmarkAdd-4 2000000000 0.51 ns/op # 循环次数足够 BenchmarkSleep-4 5 1001927937 ns/op # 次数不足,延长执行时间 ok test 7.103s # 同样通过调整B.N重新测试

timer

如果在测试函数中要执行一些额外操作,那么应该临时阻止计时器工作。

func BenchmarkAdd(b*testing.B) { time.Sleep(time.Second) b.ResetTimer() // 重置

for i:=0;i<b.N;i++ { _ =add(1,2)

   if i==1{ 
       b.StopTimer()           // 暂停 
       time.Sleep(time.Second) 
       b.StartTimer()          // 恢复 
    } 
} 

}

memory

性能测试关心的不仅仅是执行时间,还包括在堆上的内存分配,因为内存分配和垃圾回收的相关操作也应计入消耗成本。

func heap() []byte{ return make([]byte,1024*10) }

func BenchmarkHeap(b*testing.B) { for i:=0;i<b.N;i++ { _ =heap() } }

输出:

$go test-bench. -benchmem-gcflags”-N-l” # 禁用内联和优化,以便观察结果

BenchmarkHeap-4 1000000 2392 ns/op 10496 B/op 1 allocs/op ok test 2.424s

输出结果包括单次执行堆内存分配总量和次数。

也可将测试函数设置为总是输出内存分配信息,无论使用benchmem参数与否。

func BenchmarkHeap(b*testing.B) { b.ReportAllocs() b.ResetTimer()

for i:=0;i<b.N;i++ { _ =heap() } }